﻿using System;
using System.Collections.Generic;
using System.IO;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;

namespace NetTopologySuite.GeometriesGraph
{
	public class PlanarGraph
	{
		public static void LinkResultDirectedEdges(IList<Node> nodes)
		{
			foreach (Node node in nodes)
			{
				((DirectedEdgeStar)node.Edges).LinkResultDirectedEdges();
			}
		}

		public PlanarGraph(NodeFactory nodeFact)
		{
			this._nodes = new NodeMap(nodeFact);
		}

		public PlanarGraph()
		{
			this._nodes = new NodeMap(new NodeFactory());
		}

		public IEnumerator<Edge> GetEdgeEnumerator()
		{
			return this._edges.GetEnumerator();
		}

		public IList<EdgeEnd> EdgeEnds
		{
			get
			{
				return this.edgeEndList;
			}
		}

		protected internal IList<Edge> Edges
		{
			get
			{
				return this._edges;
			}
		}

		public bool IsBoundaryNode(int geomIndex, Coordinate coord)
		{
			Node node = this._nodes.Find(coord);
			if (node == null)
			{
				return false;
			}
			Label label = node.Label;
			return label != null && label.GetLocation(geomIndex) == Location.Boundary;
		}

		protected void InsertEdge(Edge e)
		{
			this._edges.Add(e);
		}

		public void Add(EdgeEnd e)
		{
			this._nodes.Add(e);
			this.edgeEndList.Add(e);
		}

		public IEnumerator<Node> GetNodeEnumerator()
		{
			return this._nodes.GetEnumerator();
		}

		public IList<Node> Nodes
		{
			get
			{
				return new List<Node>(this._nodes.Values);
			}
		}

		protected NodeMap NodeMap
		{
			get
			{
				return this._nodes;
			}
		}

		public Node AddNode(Node node)
		{
			return this._nodes.AddNode(node);
		}

		public Node AddNode(Coordinate coord)
		{
			return this._nodes.AddNode(coord);
		}

		public Node Find(Coordinate coord)
		{
			return this._nodes.Find(coord);
		}

		public void AddEdges(IList<Edge> edgesToAdd)
		{
			foreach (Edge edge in edgesToAdd)
			{
				this._edges.Add(edge);
				DirectedEdge directedEdge = new DirectedEdge(edge, true);
				DirectedEdge directedEdge2 = new DirectedEdge(edge, false);
				directedEdge.Sym = directedEdge2;
				directedEdge2.Sym = directedEdge;
				this.Add(directedEdge);
				this.Add(directedEdge2);
			}
		}

		public void LinkResultDirectedEdges()
		{
			foreach (Node node in this.Nodes)
			{
				((DirectedEdgeStar)node.Edges).LinkResultDirectedEdges();
			}
		}

		public void LinkAllDirectedEdges()
		{
			foreach (Node node in this.Nodes)
			{
				((DirectedEdgeStar)node.Edges).LinkAllDirectedEdges();
			}
		}

		public EdgeEnd FindEdgeEnd(Edge e)
		{
			foreach (EdgeEnd edgeEnd in this.edgeEndList)
			{
				if (edgeEnd.Edge == e)
				{
					return edgeEnd;
				}
			}
			return null;
		}

		public Edge FindEdge(Coordinate p0, Coordinate p1)
		{
			for (int i = 0; i < this._edges.Count; i++)
			{
				Edge edge = this._edges[i];
				Coordinate[] coordinates = edge.Coordinates;
				if (p0.Equals(coordinates[0]) && p1.Equals(coordinates[1]))
				{
					return edge;
				}
			}
			return null;
		}

		public Edge FindEdgeInSameDirection(Coordinate p0, Coordinate p1)
		{
			for (int i = 0; i < this._edges.Count; i++)
			{
				Edge edge = this._edges[i];
				Coordinate[] coordinates = edge.Coordinates;
				if (PlanarGraph.MatchInSameDirection(p0, p1, coordinates[0], coordinates[1]))
				{
					return edge;
				}
				if (PlanarGraph.MatchInSameDirection(p0, p1, coordinates[coordinates.Length - 1], coordinates[coordinates.Length - 2]))
				{
					return edge;
				}
			}
			return null;
		}

		private static bool MatchInSameDirection(Coordinate p0, Coordinate p1, Coordinate ep0, Coordinate ep1)
		{
			return p0.Equals(ep0) && (CGAlgorithms.ComputeOrientation(p0, p1, ep1) == 0 && QuadrantOp.Quadrant(p0, p1) == QuadrantOp.Quadrant(ep0, ep1));
		}

		public void WriteEdges(StreamWriter outstream)
		{
			outstream.WriteLine("Edges:");
			for (int i = 0; i < this._edges.Count; i++)
			{
				outstream.WriteLine("edge " + i + ":");
				Edge edge = this._edges[i];
				edge.Write(outstream);
				edge.EdgeIntersectionList.Write(outstream);
			}
		}

		private readonly List<Edge> _edges = new List<Edge>();

		private readonly NodeMap _nodes;

		protected IList<EdgeEnd> edgeEndList = new List<EdgeEnd>();
	}
}
